-
Notifications
You must be signed in to change notification settings - Fork 333
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Asciidoctor: Add support for inline callouts #566
Conversation
Adds support for inline callout in a fairly hacky way. These look like: ``` ---- POST <1> /_search/scroll <2> ---- <1> words <2> other words ``` They are supported by asciidoc and the elasticsearch reference uses a few of them and they look *great*. Asciidoctor doesn't support them and doesn't have a nice extension point to add them so we hack asciidoctor fairly shamelessly to get them in. This cuts the number of failures when building the Elasticsearch reference down from "then entire screen" to 3.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some questions here and there, overall I feel probably less offended than the comments seem like they expect. It would be ideal if upstream supported the feature instead though.
RSpec.describe InlineCallout do | ||
it "enables support for inline callouts if requested" do | ||
attributes = { | ||
'inline-callouts' => '', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What meaning does ''
have as a value? In many languages that value is false-y.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In asciidoctor flags attributes are usually controlled by being defined at all. It is weird, but it feels more like the way the rest of asciidoctor works.
# POST <1> /_search/scroll <2> | ||
# | ||
# NOTE: This isn't an asciidoctor extension so much as a hack. Just including | ||
# the file causes us to hack asciidoctor to enable the behavior. By default we |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be more descriptive than just saying "hack" twice?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah.
# to get this into Asciidoctor proper. These methods are basically the same | ||
# as the methods in asciidoctor but with new regexes. | ||
old_verbose = $VERBOSE | ||
$VERBOSE = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the actual scope of this change? If this extension is loaded, does the altering of logging only occur for inline callouts, or is loading this extension going to affect logging levels for the duration of the run? I'm afraid I know too little about ruby evaluation and loading rules to just know this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is global but I restore it after the fact. I think threads could be an issue here, but we're super single threaded at this point. I don't like it much though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought for a while about asking if it could be guarded by a check for the inline-callouts
argument, but then I think I have decided that basically we're always going to have that on, because we use them (or else this extension wouldn've have been needed to begin with) so it doesn't matter for us.
Which in turn means that we are always going to have log warnings suppressed, which is why I am so concerned about understanding the scope of how much time a complete run spends with the log levels tampered with vs. not.
Which brings me to ask whether patching instances is preferable, possibly in combination with moving the verbose tweak inside the overridden method, to ensure the scope is exactly what you're really after?
autonum = 0 | ||
callout_rx = (document.attr? 'inline-callouts') ? InlineCalloutScanRx : CalloutScanRx | ||
text.scan(callout_rx) { | ||
# lead with assignments for Ruby 1.8.7 compat |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I doubt 1.8.7 is going to be a concern when asciidoctor requires >= 2.3
And if the answer is that we use 1.8.7 somewhere then I'm going to go ahead now and ask what our plan is to stop that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comes from lifting stuff out of asciidoctor. I'll clean it up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly I like the line even without 1.8.7 because it is easier to read. I think, at least.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've pushed a couple of cleanups and answered your questions.
It does look like monkeypatching is "the way to do this" now.
autonum = 0 | ||
callout_rx = (document.attr? 'inline-callouts') ? InlineCalloutScanRx : CalloutScanRx | ||
text.scan(callout_rx) { | ||
# lead with assignments for Ruby 1.8.7 compat |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly I like the line even without 1.8.7 because it is easier to read. I think, at least.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this extension is effectively addressing a difference of behavior between asciidoc
and asciidoctor
, let me raise this question:
What is the asciidoctor recommended way to achieve what the documentation is trying to do when using this feature?
Put another way, can we get around supporting this by not needing it anymore?
# NOTE: This isn't an asciidoctor extension so much as a hack. Just including | ||
# the file causes us to monkey patch its code into asciidoctor. By default we | ||
# don't change asciidoctor, but if you set the `inline-callouts` attribute so | ||
# you need to *ask* for the change in behavior. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know I am kind of reviewing with the -pedantic
flag on here but... I still have some issues.
- The new sentence no longer really makes sense.
By default we don't change asciidoctor, but if you set the
inline-callouts
attribute so you need to ask for the change in behavior.
If it helps read it out loud. It's not conveying a coherent meaning.
- It kind of tells a lie.
By default we don't change asciidoctor
As also discussed below, logging behavior is being altered too, and that behavior change is not guarded by any check against (attr? 'inline-callouts')
either, it always does it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logging behavior is only altered for the duration of the monkey patching which is right at startup. We restore the old verbose mode after completing the patch.
I'll see about rewriting the sentence.
@@ -6,6 +6,9 @@ | |||
require_relative 'elastic_compat_preprocessor/extension' | |||
require_relative 'elastic_include_tagged/extension' | |||
|
|||
# This extensions is special because it is evil - just loading it is enough | |||
require_relative 'inline_callout/extension' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then is it possible to only even load it when inline-callouts
is defined?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd be possible but strange because we load these before we have a document to check what is defined. We'd have to load late which is indeed strange.
found = false | ||
autonum = 0 | ||
callout_rx = (document.attr? 'inline-callouts') ? InlineCalloutScanRx : CalloutScanRx | ||
text.scan(callout_rx) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the body here duplicated from what you are patching over? In particular if inline-callouts
is not specified, I mean. Point being, in the absence of inline-callouts
can this delegate to the normal implementation? (I'm probably showing how much ruby I don't really do, here...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can delegate, yeah. I wasn't partially because I figured this might be what it'd look like to make the change in asciidoctor itself. But it looks like they won't want this change until at least 2.0, which won't look like this anyway. So I can just delegate.
} | ||
end | ||
|
||
def callout_rx |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I freely admit this is picky, but this is both a) defined after it is used, and b) defined after a previous block of code also defined a different variable with the exact same name. I know it made me have to squint at it twice as a result. Presuming the order doesn't matter, and I'd be horrified if it did, I think it would read more clearly if it were swapped in order with sub_callouts
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The change is actually to a different place. This one is in Substitutors
module and the other is in the Parser
class. I have them in this order because it is the order in which they are run so it made sense to me. Would it be less confusing if the method were a variable? I can do that, it just didn't look as nice to me.
# use sub since it might be behind a line comment | ||
$&.sub(RS, '') | ||
else | ||
Inline.new(self, :callout, $4 == '.' ? (autonum += 1).to_s : $4, :id => @document.callouts.read_next_id, :attributes => { 'guard' => $1 }).convert |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line is too long and too complicated, it has a ?:
inside the argument list, it uses magic gsub numbers, it has a hash assignment inside of a hash assignment...
Formatting may be enough to address most of this, a variable or two might also be helpful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I comes from upstream so I dind't touch it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't have written it this way though.
# to get this into Asciidoctor proper. These methods are basically the same | ||
# as the methods in asciidoctor but with new regexes. | ||
old_verbose = $VERBOSE | ||
$VERBOSE = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought for a while about asking if it could be guarded by a check for the inline-callouts
argument, but then I think I have decided that basically we're always going to have that on, because we use them (or else this extension wouldn've have been needed to begin with) so it doesn't matter for us.
Which in turn means that we are always going to have log warnings suppressed, which is why I am so concerned about understanding the scope of how much time a complete run spends with the log levels tampered with vs. not.
Which brings me to ask whether patching instances is preferable, possibly in combination with moving the verbose tweak inside the overridden method, to ensure the scope is exactly what you're really after?
Per https://asciidoctor.org/docs/user-manual/#callouts "The callout number (at the target) must be placed at the end of the line." In my opinion, the value of inline callouts is not high. We could fix the source files to have both callouts at the end of the line as asciidoctor requires. |
I think it works pretty well in places like sql's percentile rank docs. But for the most part I don't want to have to go and rewrite docs in a bunch of branches just to be compatible with Asciidoctor. Maybe it is fine in this case, but I've been trying to avoid it in general because I feel like it'd be very hard to enforce until we switch to asciidoctor which gets us better tools. |
I feel like I kind of disagree though. Given that asciidoc is EOL and all users are directed to move to asciidoctor, excessive* backward compat work toward a dead toolchain will only result in not only never returning to fix old idioms but also continuing to author more documentation source which also relies on a dead toolchain. Weaning off of things asciidoctor does not (and will not, and looking at the issue in their repo, this feature seems to fall in that category at least for the plan-able future) do is the best path out of that. The value in these extensions is in a low-disruption transition period, but preventing ever transitioning off of asciidoc-not-asciidoctor is adding a needless code support burden in perpetuity for an infinitely diminishing return. * The word 'excessive' is important here, it is crucial that we keep revisiting our understanding of where the line lies between "necessary and useful" and "too much". Edit: if the deeper concern is that it is hard to enforce, we already have test builds for asciidoctor runs, we actually do have the capability to detect that case; the open question is how we want to communicate that during the time we're in transition and using asciidoc for the primary artifacts still. |
Adds support for inline callout in a fairly hacky way. These look like:
They are supported by asciidoc and the elasticsearch reference uses a
few of them and they look great. Asciidoctor doesn't support them and
doesn't have a nice extension point to add them so we hack asciidoctor
fairly shamelessly to get them in.
This cuts the number of failures when building the Elasticsearch
reference down from "then entire screen" to 3.